﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Ue.CQRS.Results;
using Ue.CQRS.DistributionManager;
using Ue.CQRS.Repositories;
using Ue.CQRS.Events;

namespace Ue.CQRS.Tests.DistributionManager
{
    [TestClass]
    public class IDistributorExtensionTests
    {
        private IDistributor distributor;
        private ConcurrentDictionary<string, ConcurrentQueue<Action<IEvent>>> eventHandlerCaches;
        private Mock<IRepository<string, ConcurrentQueue<Action<IEvent>>>> mockRepository;

        [TestInitialize]
        public void Initial()
        {
            this.eventHandlerCaches = new ConcurrentDictionary<string, ConcurrentQueue<Action<IEvent>>>();

            this.mockRepository = new Mock<IRepository<string, ConcurrentQueue<Action<IEvent>>>>();
            this.mockRepository.Setup(item => item.GetOrAdd(It.IsAny<string>(), It.IsAny<Func<string, ConcurrentQueue<Action<IEvent>>>>()))
                .Returns<string, Func<string, ConcurrentQueue<Action<IEvent>>>>((eventId, queue) => this.eventHandlerCaches.GetOrAdd(eventId, queue));

            this.distributor = new Distributor(this.mockRepository.Object);
        }

        #region Register By Assembly or Type

        [TestMethod]
        public void OnlyRegisterClass()
        {
            this.distributor.Register(typeof(IEventHandler));
            Assert.AreEqual(0, this.eventHandlerCaches.Count);

            this.distributor.Register(typeof(string));
            Assert.AreEqual(0, this.eventHandlerCaches.Count);
        }

        [TestMethod]
        public void RegisterByType()
        {
            this.distributor.Register(typeof(IDistributorExtensionTests));

            Assert.AreEqual(3, this.eventHandlerCaches.Count);
            Assert.IsTrue(this.eventHandlerCaches.ContainsKey("Event1Event"));
            Assert.IsTrue(this.eventHandlerCaches.ContainsKey("Event2Event"));
            Assert.IsTrue(this.eventHandlerCaches.ContainsKey("Event3Event"));

            Assert.AreEqual(2,this.eventHandlerCaches["Event1Event"].Count);
            Assert.AreEqual(2, this.eventHandlerCaches["Event2Event"].Count);
            Assert.AreEqual(1, this.eventHandlerCaches["Event3Event"].Count);
        }

        [TestMethod]
        public void EventHanderAttributeOnInterface()
        {
            this.distributor.Register(typeof(FakeEventHandler));

            Assert.AreEqual(1, this.eventHandlerCaches.Count);
            Assert.IsTrue(this.eventHandlerCaches.ContainsKey("Event1Event"));

            Assert.AreEqual(1, this.eventHandlerCaches["Event1Event"].Count);
        }

        [TestMethod]
        public void NotRegisterHandleInEventSource()
        {
            this.distributor.Register(Assembly.GetExecutingAssembly());

            //在这个类中共有4个Event1Event的事件处理器，其中一个在FakeEventHandler2对象中，它是事件源所以要排除
            Assert.AreEqual(3, this.eventHandlerCaches["Event1Event"].Count);
        }

        #endregion

        #region Handler

        [EventHandler]
        public void Event1Handle(DomainEvent<string> dominEvent)
        {
            //EventHandler未指定事件名
        }

        [EventHandler("Event1Event")]
        public void EventHandle(string args)
        {
            //EventHandler指定事件名
        }

        [EventHandler]
        public void Event2Handle(string args)
        {
            //常规EventArg.Data参数
        }

        [EventHandler]
        public void Event2Handle(DomainEvent<string> domainEvent)
        {
            //DomainEvent<>型参数
        }

        [EventHandler]
        public void Event3Handle(IList<string> args)
        {
            //泛型EventArg.Data参数
        }

        #endregion
    }

    public interface IEventHandler
    {
        [EventHandler]
        void Event1Handle(string str);
    }

    public class FakeEventHandler : IEventHandler
    {
        public void Event1Handle(string str)
        {
            
        }
    }

    [EventSource]
    public class FakeEventHandler2:IEventHandler
    {
        public void Event1Handle(string str)
        {
            
        }
    }

    
}
